package com.zzw.init;

import com.zzw.annotation.GlobalDestroyMethod;
import com.zzw.annotation.GlobalInitMethod;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import java.util.List;

@Component
public class GlobalInitDestroyMethodPostProcessor implements BeanDefinitionRegistryPostProcessor {
    private static final String GLOBAL_INIT_METHOD = GlobalInitMethod.class.getName();

    private static final String GLOBAL_DESTROY_METHOD = GlobalDestroyMethod.class.getName();

    private static final String GLOBAL_METHOD_NAME = "value";

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        if(beanDefinitionNames == null || beanDefinitionNames.length == 0) {
            return;
        }

        boolean hasGlobalInitMethod = false;
        boolean hasGlobalDestroyMethod = false;
        String initMethodName = "";
        String destroyMethodName = "";
        // 查找 全局初始化方法与全局销毁方法配置
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);
            if(beanDefinition instanceof ScannedGenericBeanDefinition) {
                ScannedGenericBeanDefinition scannedBeanDefinition = ScannedGenericBeanDefinition.class.cast(beanDefinition);
                String globalInitMethodName = extractGlobalMethod(scannedBeanDefinition, GLOBAL_INIT_METHOD);
                if(!StringUtils.isEmpty(globalInitMethodName)) {
                    initMethodName = globalInitMethodName;
                    hasGlobalInitMethod = true;
                }
                String globalDestroyMethodName = extractGlobalMethod(scannedBeanDefinition, GLOBAL_DESTROY_METHOD);
                if(!StringUtils.isEmpty(globalDestroyMethodName)) {
                    destroyMethodName = globalInitMethodName;
                    hasGlobalDestroyMethod = true;
                }
            }
            if(hasGlobalInitMethod && hasGlobalDestroyMethod) {
                break;
            }
        }

        // 为BeanDefinition配置全局初始化和销毁方法
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);
            if(beanDefinition instanceof AbstractBeanDefinition) {
                AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) beanDefinition;
                if(hasGlobalInitMethod) {
                    if(StringUtils.isEmpty(abstractBeanDefinition.getInitMethodName())){
                        abstractBeanDefinition.setInitMethodName(initMethodName);
                        abstractBeanDefinition.setEnforceInitMethod(false);
                    }
                }
                if(hasGlobalDestroyMethod) {
                    if(StringUtils.isEmpty(abstractBeanDefinition.getDestroyMethodName())) {
                        abstractBeanDefinition.setDestroyMethodName(destroyMethodName);
                        abstractBeanDefinition.setEnforceDestroyMethod(false);
                    }
                }
            }
        }

    }

    private String extractGlobalMethod(ScannedGenericBeanDefinition scannedBeanDefinition, String annotationName) {
        AnnotationMetadata metadata = scannedBeanDefinition.getMetadata();
        MultiValueMap<String, Object> allInitAnnotationAttributes = metadata.getAllAnnotationAttributes(annotationName);
        if(allInitAnnotationAttributes != null && allInitAnnotationAttributes.containsKey(GLOBAL_METHOD_NAME)) {
            List<Object> methodValue = allInitAnnotationAttributes.get(GLOBAL_METHOD_NAME);
            String value = (String) methodValue.get(0);
            if(StringUtils.hasText(value)) {
                return value;
            }
        }
        return null;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // do nothing
    }
}
